using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Text;
using Developex.DatabaseWrapper.Fields;

namespace Developex.DatabaseWrapper
{
	/// <summary>
	/// Foreign object
	/// </summary>
	/// <typeparam name="T">field type</typeparam>
	/// <typeparam name="FRow">Foreign object type</typeparam>
	public class ForeignObject<T, FRow> : ForeignKeyFieldBase<T, FRow> where FRow : Row, new()
	{
		#region Fields

		/// <summary>
		/// 
		/// </summary>
		protected string joinExpression = null;
		/// <summary>
		/// 
		/// </summary>
		protected IField foreignJoinField = null;
		/// <summary>
		/// 
		/// </summary>
		protected IField ownerJoinField = null;
		/// <summary>
		/// 
		/// </summary>
		protected string selectExpression = null;

		#endregion

		#region Public Properties

		/// <summary>
		/// Manually set join expression
		/// </summary>
		public string JoinExpression
		{
			get { return joinExpression; }
			set { joinExpression = value; }
		}

		/// <summary>
		/// get/set custom select expression, {0} is table name
		/// </summary>
		/// <remarks>
		/// should be set to include in SELECT query
		/// </remarks>
		public string SelectExpression
		{
			get { return selectExpression; }
			set { selectExpression = value; }
		}

		#endregion

		#region Public Functions

		/// <summary>
		/// Set join fields
		/// </summary>
		/// <param name="OwnerJoinField"></param>
		/// <param name="ForeignJoinField"></param>
		public void SetJoinFields(IField OwnerJoinField, IField ForeignJoinField)
		{
			ownerJoinField = OwnerJoinField;
			foreignJoinField = ForeignJoinField;
		}

		/// <summary>
		/// Format join expression
		/// </summary>
		/// <param name="joinOn"></param>
		/// <returns></returns>
		public string FormatJoinExpression(string joinOn)
		{
			return "LEFT JOIN [" + Object.TableName + "] [" + GetJoinPrefix() + Object.TableName + "] ON " + joinOn;
		}

		#endregion

		#region Overrides of Field

		/// <summary>
		/// Expression used in SELECT query
		/// </summary>
		public override string GetSelectExpression()
		{
			if (string.IsNullOrEmpty(selectExpression))
				return string.Empty;
			return string.Format(selectExpression, Parent.JoinTableName);
		}

		#endregion

		#region Implementation of abstract members

		///<summary>
		/// Get join expression for this field
		///</summary>
		///<returns></returns>
		public override string GetJoinExpression()
		{
			if (foreignJoinField != null && ownerJoinField != null)
			{
				return FormatJoinExpression(foreignJoinField.GetSelectExpression() + " = " + ownerJoinField.GetSelectExpression());
			}
			return string.Empty;
		}

		#endregion

		#region Constructors

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="name"></param>
		/// <param name="flags"></param>
		/// <param name="doFullLoad"></param>
		public ForeignObject(string name, FieldFlags flags, bool doFullLoad)
			: base(name, flags, doFullLoad)
		{
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="name"></param>
		/// <param name="doFullLoad"></param>
		public ForeignObject(string name, bool doFullLoad)
			: base(name, doFullLoad)
		{
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="name"></param>
		/// <param name="flags"></param>
		/// <param name="insertDefaultValue"></param>
		/// <param name="doFullLoad"></param>
		/// <param name="simpleNamingMode"></param>
		public ForeignObject(string name, T insertDefaultValue, FieldFlags flags, bool doFullLoad, bool simpleNamingMode)
			: base(name, insertDefaultValue, flags, doFullLoad, simpleNamingMode)
		{
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="name"></param>
		/// <param name="flags"></param>
		/// <param name="doFullLoad"></param>
		/// <param name="simpleNamingMode"></param>
		public ForeignObject(string name, FieldFlags flags, bool doFullLoad, bool simpleNamingMode)
			: base(name, flags, doFullLoad, simpleNamingMode)
		{
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="name"></param>
		/// <param name="doFullLoad"></param>
		/// <param name="simpleNamingMode"></param>
		public ForeignObject(string name, bool doFullLoad, bool simpleNamingMode)
			: base(name, doFullLoad, simpleNamingMode)
		{
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="name"></param>
		public ForeignObject(string name)
			: base(name)
		{
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="name"></param>
		/// <param name="flags"></param>
		public ForeignObject(string name, FieldFlags flags)
			: base(name, flags)
		{
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="name"></param>
		/// <param name="flags"></param>
		/// <param name="insertDefaultValue"></param>
		public ForeignObject(string name, FieldFlags flags, T insertDefaultValue)
			: base(name, flags, insertDefaultValue)
		{
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="name"></param>
		/// <param name="flags"></param>
		/// <param name="insertDefaultValue"></param>
		/// <param name="doFullLoad"></param>
		/// <param name="cretionParameters"></param>
		public ForeignObject(string name, T insertDefaultValue, FieldFlags flags, bool doFullLoad, object[] cretionParameters)
			: base(name, insertDefaultValue, flags, doFullLoad, cretionParameters)
		{
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="name"></param>
		/// <param name="flags"></param>
		/// <param name="doFullLoad"></param>
		/// <param name="cretionParameters"></param>
		public ForeignObject(string name, FieldFlags flags, bool doFullLoad, object[] cretionParameters)
			: base(name, flags, doFullLoad, cretionParameters)
		{
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="name"></param>
		/// <param name="doFullLoad"></param>
		/// <param name="cretionParameters"></param>
		public ForeignObject(string name, bool doFullLoad, object[] cretionParameters)
			: base(name, doFullLoad, cretionParameters)
		{
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="name"></param>
		/// <param name="flags"></param>
		/// <param name="insertDefaultValue"></param>
		/// <param name="doFullLoad"></param>
		/// <param name="simpleNamingMode"></param>
		/// <param name="cretionParameters"></param>
		public ForeignObject(string name, T insertDefaultValue, FieldFlags flags, bool doFullLoad, bool simpleNamingMode, object[] cretionParameters)
			: base(name, insertDefaultValue, flags, doFullLoad, simpleNamingMode, cretionParameters)
		{
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="name"></param>
		/// <param name="flags"></param>
		/// <param name="doFullLoad"></param>
		/// <param name="simpleNamingMode"></param>
		/// <param name="cretionParameters"></param>
		public ForeignObject(string name, FieldFlags flags, bool doFullLoad, bool simpleNamingMode, object[] cretionParameters)
			: base(name, flags, doFullLoad, simpleNamingMode, cretionParameters)
		{
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="name"></param>
		/// <param name="doFullLoad"></param>
		/// <param name="simpleNamingMode"></param>
		/// <param name="cretionParameters"></param>
		public ForeignObject(string name, bool doFullLoad, bool simpleNamingMode, object[] cretionParameters)
			: base(name, doFullLoad, simpleNamingMode, cretionParameters)
		{
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="name"></param>
		/// <param name="cretionParameters"></param>
		public ForeignObject(string name, object[] cretionParameters)
			: base(name, cretionParameters)
		{
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="name"></param>
		/// <param name="flags"></param>
		/// <param name="cretionParameters"></param>
		public ForeignObject(string name, FieldFlags flags, object[] cretionParameters)
			: base(name, flags, cretionParameters)
		{
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="name"></param>
		/// <param name="flags"></param>
		/// <param name="insertDefaultValue"></param>
		/// <param name="cretionParameters"></param>
		public ForeignObject(string name, FieldFlags flags, T insertDefaultValue, object[] cretionParameters)
			: base(name, flags, insertDefaultValue, cretionParameters)
		{
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="name"></param>
		/// <param name="flags"></param>
		/// <param name="insertDefaultValue"></param>
		/// <param name="doFullLoad"></param>
		public ForeignObject(string name, T insertDefaultValue, FieldFlags flags, bool doFullLoad)
			: base(name, insertDefaultValue, flags, doFullLoad)
		{
		}

		#endregion

		#region ICloneable

		///<summary>
		/// Create object 
		///</summary>
		///<returns></returns>
		/// <remarks>
		/// Here should be only object creation, all data copy should be in overrided Clone function.
		/// Used only in base version of Clone function.
		/// Should be overrided for ICloneable interface.
		/// </remarks>
		protected override Field<T> CreateObjectForClone()
		{
			return new ForeignObject<T, FRow>(null);
		}

		/// <summary>
		/// Clone
		/// </summary>
		/// <returns></returns>
		public override object Clone()
		{
			ForeignObject<T, FRow> field = (ForeignObject<T, FRow>)base.Clone();
			field.joinExpression = joinExpression;
			field.selectExpression = selectExpression;
			// Note: light-weight clone
			field.foreignJoinField = foreignJoinField;
			field.ownerJoinField = ownerJoinField;
			return field;
		}

		#endregion
	}
}
